home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1994 / MacHack 1994.toast / MacHack™ 1987-1994 / MacHack™ '91 / '91 Attendee Contributions / Rotate Src / Rotate.ac < prev    next >
Encoding:
Text File  |  1991-06-20  |  14.9 KB  |  553 lines  |  [TEXT/MPS ]

  1. ;    Bitmap rotation routines
  2.  
  3. ;    Copyright © 1989 Bob Spence
  4. ;    All rights reserved.
  5.  
  6.     INCLUDE 'QuickEqu.a'
  7.     INCLUDE 'Traps.a'
  8.  
  9. ;
  10. ; Rotation States
  11. ;
  12. ;    no rotation        rotate 180
  13. ;    (0)    1 2            (X)    4 3
  14. ;        3 4                2 1
  15. ;
  16. ;    mirror            mirror
  17. ;    horizontal        vertical
  18. ;    (H)    2 1            (V)    3 4
  19. ;        4 3                1 2
  20. ;
  21. ;    rotate left        rotate right
  22. ;    (L)    2 4            (R    3 1
  23. ;        1 3                4 2
  24. ;
  25. ;     flip left         flip right
  26. ;  (FL)    1 3           (FR)    4 2
  27. ;        2 4                3 1
  28. ;
  29.  
  30. ;
  31. ;    Rotation State Table
  32. ;
  33. ;              Rotate  Mirror  Mirror  Rotate  Rotate
  34. ;      State       180      Horiz       Vert       Left      Right        FL        FR
  35. ;        0        X        H        V        L        R        FL        FR
  36. ;        H        V        0        X        FL        FR        L        R
  37. ;        V        H        X        0        FR        FL        R        L
  38. ;        X        0        V        H        R        L        FR        FL
  39. ;        L        R        FR        FL        X        0        H        V
  40. ;        R        L        FL        FR        0        X        V        H
  41. ;       FL        FR        R        L        V        H        0        X
  42. ;       FR        FL        L        R        H        V        X        0
  43. ;
  44.  
  45. ; typedef enum {Rot0, RotH, RotV, RotX,
  46. ;                RotL, RotR, RotFR, RotFL} RotationSw;
  47.  
  48. Rot0    EQU    0
  49. RotH    EQU    1
  50. RotV    EQU    2
  51. RotX    EQU    3
  52. RotL    EQU    4
  53. RotR    EQU    5
  54. RotFL    EQU    6
  55. RotFR    EQU    7
  56.  
  57. ; PROCEDURE CalcRotate(OldState, NewRotate: RotationSw): RotationSw;
  58. ; parameter offsets (in Pascal order)
  59. NEW_ROTATE    EQU 4+4
  60. OLD_STATE    EQU NEW_ROTATE+4
  61.  
  62.     EXPORT    CALC_ROTATE
  63. CALC_ROTATE    PROC
  64.     Link    A6,#0
  65.     Move.L    OLD_STATE(A6),D0     ; Get the rotation state
  66.     Lsl.W    #3,D0
  67.     Add.W    NEW_ROTATE(A6),D0 ; Apply the new rotation
  68.     And.W    #$3F,D0
  69.     Move.L    RotState,A0        ; Point to the transformation table
  70.     Move.B    (A0,D0.W),-2(A6) ; Return the resultant rotation state
  71.     Unlk    A6
  72.     Move.L    (sp)+,A0        ; Get return address and
  73.     Add.L    #8,sp            ; pop parameters off stack
  74.     Jmp        (A0)            ; for a Pascal style return
  75.  
  76. RotState    ; Indexed by state and rotation, giving new rotation state
  77.     DC.B     Rot0, RotX, RotH, RotV, RotL, RotR, RotFL,RotFR
  78.     DC.B    RotH, RotV, Rot0, RotX, RotFL,RotFR,RotL, RotR
  79.     DC.B    RotV, RotH, RotX, Rot0, RotFR,RotFL,RotR, RotL
  80.     DC.B    RotX, Rot0, RotV, RotH, RotR, RotL, RotFR,RotFL
  81.     DC.B    RotL, RotR, RotFR,RotFL,RotX, Rot0, RotH, RotV
  82.     DC.B    RotR, RotL, RotFL,RotFR,Rot0, RotX, RotV, RotH    
  83.     DC.B    RotFL,RotFR,RotR, RotL, RotV, RotH, Rot0, RotX
  84.     DC.B    RotFR,RotFL,RotL, RotR, RotH, RotV, RotX, Rot0
  85.  
  86.     ENDPROC
  87.     
  88. ;    The user may also select rotation of the image 90° left, right, or
  89. ;     180° as well as mirroring of the image horizontally and vertically.
  90.  
  91.  
  92.  
  93. ; Register usage
  94. ;    A0 = Input bitmap or current data pointer
  95. ;    A1 = Output bitmap or current data pointer
  96. ;    D0 & D1 = temp variables
  97. ;    D2..D7  = part of 16 word rotation matrix
  98.  
  99. ; Named register storage
  100.  
  101. InBase        EQU A2            ; Input BitMap row base address (when rotated)
  102. OutBase        EQU A2            ; Output BitMap row base address (when mirrored)
  103. InBytes        EQU A3            ; Input BitMap rowBytes
  104.  
  105. ; Local variable stack offsets
  106.  
  107. RowPtr        EQU    -4            ; Temp storage for output word pointer
  108. LowBound    EQU    RowPtr-4    ; Lowest output word address
  109. HiBound        EQU    LowBound-4    ; Highest output word address
  110. OutBytes    EQU    HiBound-2    ; Output BitMap rowBytes 
  111. InWords        EQU    OutBytes-2    ; Words in an input row (-1 for Dbra))
  112. InRows        EQU    InWords-2    ; Rows in the input BitMap
  113. OutRows        EQU    InRows-2    ; Rows in the output BitMap
  114. WordCnt        EQU    OutRows-2    ; Temp storage for Word loop counter
  115. xD0            EQU    WordCnt-2    ; Temp storage for D0 in D0..D7 data block
  116. LOCALSZ        EQU    xD0-2        ; Total space needed for local variables
  117.  
  118. ; The following Macros are used to:
  119. ;    Initialize the rotation loops
  120. ;    Load registers with input to transform
  121. ;    Transform the data
  122. ;    Loop thru the data as needed, and return
  123.  
  124. ;    
  125. ; Macros used to rotate a BitMap [left, right, flip left, flip right]
  126. ;
  127. ; Used by LOAD_REGS to fill a D register with the next 2 column words
  128.     MACRO
  129.     LOAD_REG    &Dreg,&swap
  130.     IF &swap = 'T' THEN
  131.     Swap    &Dreg            ; Put it into hi word
  132.     ENDIF
  133.     Move.W    (A0),&Dreg        ; Get a word
  134.     Add.L    InBytes,A0        ; Drop to next row
  135.     Swap    &Dreg            ; Put next into other word
  136.     Move.W    (A0),&Dreg        ; Get a word
  137.     Add.L    InBytes,A0        ; Drop to next row
  138.     IF &swap = 'F' THEN
  139.     Swap    &Dreg            ; Restore 1st word low, 2nd word hi
  140.     ENDIF
  141.     ENDM
  142.  
  143. ; Establish the tops of the 16 word block rotate loops and
  144. ; load a 16 word block of data to rotate in the D registers
  145.     MACRO
  146.     LOAD_REGS &Loop,&swap
  147.     Move.L    A1,RowPtr(A6)    ; Save output's word address
  148. &Loop.Col:                    ; Top of the column loop
  149.     Move.W    InWords(A6),D0    ; Word count to process, this row
  150. &Loop.Row:                    ; Top of the row loop
  151.     Move.W    D0,WordCnt(A6)    ; Save loop counter, we need D0
  152.     Move.L    A0,InBase        ; Save the current input address
  153.     ; generate LOAD_REG D0..D7
  154.     LCLA    &n
  155.     IF &swap = 'T' THEN
  156. &n    SETA    7
  157.     WHILE    &n >= 0 DO
  158.     LOAD_REG D&n,&swap 
  159. &n    SETA    &n - 1
  160.     ENDW
  161.     ELSE
  162. &n    SETA    0
  163.     WHILE    &n <= 7 DO
  164.     LOAD_REG D&n,&swap 
  165. &n    SETA    &n + 1
  166.     ENDW
  167.     ENDIF
  168.     Add.L    #2,InBase        ; Point to next input word block
  169.     Move.L    InBase,A0        ; Restore the start of input
  170.     ENDM
  171.  
  172. ; Used by END_ROTATE to store the column words
  173.     MACRO
  174.     STORE_REG    &Dreg,&AdSb1,&Loop,&swap
  175.     IF &swap = 'T' THEN
  176.     Swap    &Dreg            ; Put it into hi word
  177.     ENDIF
  178.     Move.W    &Dreg,(A1)        ; Store the rotated output word
  179.     &AdSb1..W OutBytes(A6),A1 ; Add/Subtract to next output row
  180.     IF &AdSb1 = 'Add' THEN
  181.     CMP.L    HiBound(A6),A1    ; If slop on the bottom been exceeded,
  182.     BGT        &Loop.End        ;  we will go to next column
  183.     ELSE
  184.     CMP.L    LowBound(A6),A1    ; If slop on the bottom been rotated up,
  185.     BLT        &Loop.End        ;  we will go to next column
  186.     ENDIF
  187.     Swap    &Dreg            ; Get the other word
  188.     Move.W    &Dreg,(A1)        ; Store the rotated output word
  189.     &AdSb1..W OutBytes(A6),A1 ; Add/Subtract to next output row
  190.     IF &AdSb1 = 'Add' THEN
  191.     CMP.L    HiBound(A6),A1    ; If slop on the bottom been exceeded,
  192.     BGT        &Loop.End        ;  we will go to next column
  193.     ELSE
  194.     CMP.L    LowBound(A6),A1    ; If slop on the bottom been rotated up,
  195.     BLT        &Loop.End        ;  we will go to next column
  196.     ENDIF
  197.     ENDM
  198.  
  199. ; Rotate each bit of word in D&in into the D&out result word
  200.     MACRO
  201.     RotREG &in,&out,&swapin,&swapout
  202.     LCLA    &rin
  203.     LCLA    &rout
  204. &rin SETA    &in / 2
  205. &rout SETA    &out / 2
  206.     LCLA    &diff
  207. &diff SETA    &out - &in
  208.     IF &diff >= 0 THEN
  209.     IF &swapin = 'T' THEN
  210.     Addx.W    D0,D0            ; Get an input bit
  211.     ELSE
  212.     Addx.W    D&rin,D&rin        ; Get an input bit
  213.     ENDIF
  214.     IF &swapout = 'T' THEN
  215.     Swap    D&rout            ; Get the 2nd, hi word
  216.     Addx.W    D&rout,D&rout    ; Set the output bit
  217.     Swap    D&rout            ; Restore hi and lo words
  218.     ELSE
  219.     Addx.W    D&rout,D&rout    ; Set the output bit
  220.     ENDIF
  221.     ENDIF
  222.     ENDM
  223.  
  224. ; Get 16 bits from the 16 Dx words, rotated into D0
  225.     MACRO
  226.     ROT_REGS
  227.     ; generate RotREG D0..D7
  228.     LCLA    &in
  229.     LCLA    &out
  230.     LCLA    &x
  231. &in    SETA    0
  232.     WHILE    &in <= 15 DO
  233. &out SETA    0
  234. &x SETA        &in / 2
  235.     Addx.W    D&x,D&x            ; init the x flag, but insert some trash
  236.     WHILE    &out <= 15 DO
  237.     RotREG    &in,&out,F,F
  238. &out SETA    &out + 1
  239.     RotREG    &in,&out,F,T
  240. &out SETA    &out + 1
  241.     ENDW
  242. &in SETA    &in + 1
  243.     Addx.W    D&x,D&x            ; clear the x flag trash
  244. &out SETA    0
  245.     Swap    D&x
  246.     Move.W    D0,xD0(A6)
  247.     Move.W    D&x,D0
  248.     Swap    D&x
  249.     Addx.W    D0,D0                ; init the x flag, but insert some trash
  250.     WHILE    &out <= 15 DO
  251.     RotREG    &in,&out,T,F
  252. &out SETA    &out + 1
  253.     RotREG    &in,&out,T,T
  254. &out SETA    &out + 1
  255.     ENDW
  256. &in SETA    &in + 1
  257.     Addx.W    D0,D0                ; clear the x flag trash
  258.     Swap    D&x
  259.     Move.W    D0,D&x
  260.     Move.W    xD0(A6),D0
  261.     Swap    D&x
  262.     ENDW
  263.     ENDM
  264.  
  265. ; Check the word, row and column loops, cleanup, and return
  266.     MACRO
  267.     END_ROTATE &Loop,&AdSb1,&AdSb2,&swap
  268.     LCLA    &n
  269. &n    SETA    0
  270.     WHILE    &n <= 7 DO
  271.     STORE_REG D&n,&AdSb1,&Loop,&swap
  272. &n    SETA    &n + 1
  273.     ENDW
  274. &Loop.End
  275.     Move.W    WordCnt(A6),D0    ; Get row width word loop count
  276.     Dbra    D0,&Loop.Row
  277.     &AdSb2..L #2,RowPtr(A6)    ; Add/Sub to next output word of row
  278.     Move.L    RowPtr(A6),A1    ; Restore to start of next output word
  279.     Move.L    InBytes,D0        ; Get the input rowBytes
  280.     Lsl.L    #4,D0            ; A0 has wrapped to 1st column, 2nd word
  281.     Sub.L    InBytes,D0        ; so offset = 15 * InBytes
  282.     Add.L    D0,A0            ; Point to next A0 in word 15 rows beyond
  283.     Sub.W    #16,InRows(A6)    ; Drop down to rotate next 16 rows
  284.     Bgt        &Loop.Col        ; When the rows are zero, we're done
  285.     ENDM
  286. ;
  287. ; Macros to mirror BitMaps [horizontal, vertical, 180°, null]
  288. ;
  289. ; Establish the tops of the mirroring loops
  290.     MACRO
  291.     GET_REG &Loop
  292. &Loop.Col:
  293.     Move.L    A1,OutBase        ; Save the start of output
  294.     Move.W    D1,D2            ; Set the Width loop count
  295. &Loop.Row:
  296.     ENDM
  297.  
  298. ; Transfer a word from input to output, unchanged
  299.     MACRO
  300.     GET_WORD    
  301.     Move.W    (A0)+,D0        ; Just get an input word, it's Ok as is
  302.     Move.W    D0,(A1)+        ; Store the word
  303.     ENDM
  304.  
  305. ; Transfer a word from input to output, bits reversed
  306.     MACRO
  307.     REVERSE_REG    
  308.     Move.W    (A0)+,D3        ; Reverse the 16 bits in word D3 
  309.     LCLA    &n
  310. &n    SETA    0
  311.     WHILE    &n <= 15 DO
  312.     Roxr.W    #1,D3            ; Take a bit from the right
  313.     Roxl.W    #1,D0            ;  and insert it to the left
  314. &n    SETA    &n + 1
  315.     ENDW
  316.     Move.W    D0,-(A1)        ; Store the word
  317.     ENDM
  318.  
  319. ; Check the word, row and column loops, cleanup, and return
  320.     MACRO
  321.     END_MIRROR &Loop,&ASub
  322.                             ; Loop,Add/Sub
  323.     Dbra    D2,&Loop.Row    ; Loop thru row's width
  324.     Move.L    OutBase,A1        ; Restore to start of row
  325.     &ASub..L InBytes,A1        ; Add/Sub to next row
  326.     Sub.W    #1,InRows(A6)    ; Drop to next row
  327.     Bgt.s    &Loop.Col        ; When the rows are zero, we're done
  328.     ENDM
  329.  
  330. ; Shift words left by the slop bits    
  331.     MACRO
  332.     SHIFT_WORDS
  333.     LCLA    &n
  334. &n    SETA    0
  335.     WHILE    &n <= 7 DO
  336.     Move.W    -(A0),D0    ; Get a word in the low word
  337.     Rol.L    D4,D0        ; Shift out the slop bits, round in good ones
  338.     Move.W    D0,(A0)        ; Store good word
  339.     Rol.L    D3,D0        ; Shift remaining good bits to top of long word
  340. &n    SETA    &n + 1
  341.     ENDW
  342.     ENDM    
  343.  
  344. doROT_REGS     PROC
  345.     ROT_REGS
  346.     Rts
  347.     ENDPROC
  348.  
  349. ;
  350. ; The bit rotation routine
  351. ; PROCEDURE Rotate (sourceMap, destMap: bitMap);
  352. ;
  353. ; Initialize loops, jump to specific rotation macros, and return
  354. ;
  355. ; parameter offsets (in Pascal order)
  356. DEST_MAP    EQU 4+4
  357. SOURCE_MAP    EQU DEST_MAP+4
  358. ROTATION    EQU SOURCE_MAP+4
  359.  
  360. Btop         EQU  D1     ; Some BitMap bounds Rect defines
  361. Bleft        EQU  D2    
  362. Bbottom        EQU  D3
  363. Bright        EQU  D4
  364.  
  365.     EXPORT    ROTATE
  366. ROTATE    PROC
  367.     Link    A6,#LOCALSZ              ; Allocate temporary storage
  368.     Movem.L    D2-D7/A2-A5,-(sp)     ; D0-D1/A0-A1 are scratch in Pascal and C
  369.     Move.L    SOURCE_MAP(A6),A2     ; Get the source BitMap address
  370.     Move.L    DEST_MAP(A6),A1          ; Get the destination BitMap address
  371.     Moveq    #0,D0
  372.     Move.L    D0,D1
  373.     Move.W    rowBytes(A2),D1        ; Get the input rowBytes
  374.     Move.L    D1,InBytes            ; Set the row byte count
  375.     Lsr.W    #1,D1                ; Convert to words
  376.     Sub.W    #1,D1                ;  minus 1 (for Dbra)
  377.     Move.W    D1,InWords(A6)
  378.     Movem.W    bounds(A2),D1-D4    ; Get the source bounds Rect
  379.     Move.W    Bbottom,D0
  380.     Sub.W    Btop,D0
  381.     Move.W    D0,InRows(A6)        ; Set the input number of rows
  382.     Cmp.B    #RotL,ROTATION(A6)    ; Is the destination not rotated
  383.     Blt.s    NoRotate
  384.     Sub.W    Bright,D0            ; Rotate the bounds Rect
  385.     Add.W    Bleft,D0            ; (Bbottom - Btop) - (Bright - Bleft);
  386.     Sub.W    D0,Bbottom
  387.     Add.W    D0,Bright
  388. NoRotate
  389.     Movem.W    D1-D4,bounds(A1) ; Set the destination bounds Rect
  390.     Move.W    Bbottom,D5
  391.     Sub.W    Btop,D5
  392.     Move.W    D5,OutRows(A6)    ; Set the destination row count
  393.  
  394.     Move.W    Bright,D6        ; Compute rowBytes
  395.     Sub.W    Bleft,D6        ;  forced to word boundary
  396.     Add.W    #15,D6
  397.     Lsr.W    #4,D6            ; (Bright - Bleft + 15) / 16) * 2
  398.     Lsl.W    #1,D6
  399.     Move.W    D6,rowBytes(A1)    ; Set the destination rowBytes
  400.     Move.W    D6,OutBytes(A6)
  401.     Mulu    D6,D5            ; Compute destination map size
  402.     Addq    #2,D5            ;  rowBytes * (Bbottom - Btop)
  403.     Move.L    D5,D0            ;  plus a fudge word for slop bits
  404.     Addq    #2,D0            ;  plus one more slop word
  405.     _NewPtr                    ; allocate space for bitmap
  406.     Move.L    A0,baseAddr(A1)    ; Set the destination upper left corner
  407.     Move.L    A0,LowBound(A6)    ; Save lowest output address
  408.     Add.L    D5,A0            ; Point beyond lower right corner
  409.     Move.L    A0,HiBound(A6)    ; Save Highest output address
  410.     Move.L    baseAddr(A2),A0    ; Get the source upper left corner
  411.     Move.W    InWords(A6),D1    ; Set D1 for GET_REG Macro
  412.  
  413.     Moveq    #0,D0
  414.     Move.B    ROTATION(A6),D0    ; Get the rotation type
  415.     And.W    #7,D0            ; Force to legal, We provide no error check, yet
  416.     Add.W    D0,D0
  417.     Move.W    Jump_Table(PC,D0),D0
  418.     Jmp        Jump_Table(PC,D0)
  419.  
  420. Jump_Table
  421.     DC.W    (Rotate_0 - Jump_Table)
  422.     DC.W    (Rotate_H - Jump_Table)
  423.     DC.W    (Rotate_V - Jump_Table)
  424.     DC.W    (Rotate_X - Jump_Table)
  425.     DC.W    (Rotate_L - Jump_Table)
  426.     DC.W    (Rotate_R - Jump_Table)
  427.     DC.W    (Rotate_FL - Jump_Table)
  428.     DC.W    (Rotate_FR - Jump_Table)
  429. ;
  430. ; The Rotation and Mirror Routines
  431. ;
  432. Rotate_0                        
  433.     Move.L        LowBound(A6),A1    ; Point to upper left corner
  434.     GET_REG        loop0
  435.     GET_WORD
  436.     END_MIRROR    loop0,Add
  437.     Bra            Exit_Rotate
  438.  
  439. Rotate_H
  440.     Move.L        LowBound(A6),A1    ; Point beyond upper right corner
  441.     Add.W        InBytes,A1        ; for predecrement of A1 in data stores
  442.     GET_REG        loopH
  443.     REVERSE_REG
  444.     END_MIRROR    loopH,Add
  445.     Bra            Shift_Left        ; shift image left slop bits
  446.  
  447. Rotate_V
  448.     Move.L        HiBound(A6),A1    ; Point beyond lower right corner
  449.     Sub            #2,A1            ; and never slop adjust
  450.     Sub.W        InBytes,A1
  451.     GET_REG        loopV
  452.     GET_WORD
  453.     END_MIRROR    loopV,Sub
  454.     Bra            Exit_Rotate
  455.  
  456. Rotate_X
  457.     Move.L        HiBound(A6),A1    ; Point beyond lower right corner
  458.                                 ; for predecrement of A1 in data stores
  459.     Move.L        DEST_MAP(A6),A4          ; Get the destination BitMap address
  460.     Move.W        bounds+right(A4),D3
  461.     Sub.W        bounds+left(A4),D3    ; Number of slop bits on the left
  462.     And.W        #$0F,D3                ; Any slop bits to shift in a word ?
  463.     Sub            #2,A1            ; We don't slop adjust later in Shift_Left
  464.     GET_REG        loopX
  465.     REVERSE_REG
  466.     END_MIRROR    loopX,Sub
  467.     Bra            Shift_Left        ; shift image left slop bits
  468.  
  469. Rotate_L
  470.     Move.L        HiBound(A6),A1    ; Point beyond lower right corner
  471.     Sub.W        OutBytes(A6),A1    ; Point to lower left corner
  472.     Sub            #2,A1            ; and never slop adjust
  473.     LOAD_REGS    loopL,F
  474.     Jsr            doROT_REGS    ;    l,l
  475.     END_ROTATE    loopL,Sub,Add,T
  476.     Bra            Exit_Rotate
  477.  
  478. Rotate_R
  479.     Move.L        LowBound(A6),A1
  480.     Add.W        OutBytes(A6),A1
  481.     Sub.L        #2,A1            ; Point to upper right corner
  482.     LOAD_REGS    loopR,T
  483.     Jsr            doROT_REGS    ;    l,r
  484.     END_ROTATE    loopR,Add,Sub,F
  485.     Bra            Shift_Left        ; shift image left slop bits
  486.  
  487. Rotate_FL
  488.     Move.L        LowBound(A6),A1    ; Point to upper left corner
  489.     LOAD_REGS    loopFL,F
  490.     Jsr            doROT_REGS    ;    l,l
  491.     END_ROTATE    loopFL,Add,Add,F
  492.     Bra            Exit_Rotate
  493.  
  494. Rotate_FR
  495.     Move.L        HiBound(A6),A1    ; Point beyond lower right corner
  496.     Sub.L        #2,A1            ; Point to lower right corner
  497.     Move.L        DEST_MAP(A6),A4          ; Get the destination BitMap address
  498.     Move.W        bounds+right(A4),D3
  499.     Sub.W        bounds+left(A4),D3    ; Number of slop bits on the left
  500.     And.W        #$0F,D3                ; Any slop bits to shift in a word ?
  501.     Sub            #2,A1            ; We don't slop adjust later in Shift_Left
  502.     LOAD_REGS    loopFR,T
  503.     Jsr            doROT_REGS    ;    l,r
  504.     END_ROTATE    loopFR,Sub,Sub,T
  505. ;;    Bra            Shift_Left        ; shift image left slop bits
  506.  
  507. ;
  508. ; Routines to shift the BitMap data for any slop around
  509. ; the edges caused by images having heights or widths
  510. ; which are not on word boundaries. Some rotations cause 
  511. ; extra columns or rows to be generated to the left or top
  512. ; of the BitMap, we now must push the image up to the topLeft
  513. ; corner of the Bitmap.
  514. ;
  515. ; Shift left the output BitMap any slop columns
  516. Shift_Left
  517.     Move.L    DEST_MAP(A6),A4          ; Get the destination BitMap address
  518.     Move.W    bounds+right(A4),D3
  519.     Sub.W    bounds+left(A4),D3    ; Number of slop bits on the left
  520.     And.W    #$0F,D3                ; Any slop bits to shift in a word ?
  521.     Beq.s    Exit_Rotate
  522.     Move.L    baseAddr(A4),A0    ; Get the destination upper left corner
  523.     Move.W    OutBytes(A6),D1
  524.     Move.W    OutRows(A6),D2    ; Get the output rows
  525.     Mulu    D1,D2            ; Get total byte size
  526.     Add.L    D2,A0            ; Point to lower right corner
  527.     Lsr.W    #1,D2            ; Convert to words
  528.     Moveq    #16,D4
  529.     Sub.W    D3,D4            ; Number of slop bits
  530.     Move.L    D2,D5
  531.     Lsr.L    #3,D5            ; Loop count in blocks of 8 words
  532.     Move.W    D2,D1
  533.     Neg.W    D1                ; To jump into the loop doing the 
  534.     And.W    #7,D1            ;  block boundary remainder first
  535.     Beq.s    Bit_Loop_End
  536.     Asl.W    #3,D1            ; Multiply by code block size
  537.     Jmp        Bit_loop(PC,D1)    ; Jump into the loop, part way
  538. Bit_loop
  539.     SHIFT_WORDS
  540. Bit_Loop_End
  541.     Dbra    D5,Bit_loop
  542.     
  543. Exit_Rotate
  544.     Movem.L    (sp)+,D2-D7/A2-A5
  545.     Unlk    A6
  546.     Move.L    (sp)+,A0        ; Get return address and
  547.     Add.L    #10,sp            ; pop parameters off stack
  548.     Jmp        (A0)            ; for a Pascal style return
  549.     ENDPROC
  550.     
  551.     END
  552.